/*
 * JSON Schema defines the media type "application/schema+json", a JSON based format for defining the structure of JSON data.
 * JSON Schema provides a contract for what JSON data is required for a given application and how to interact with it.
 * JSON Schema is intended to define validation, documentation, hyperlink navigation, and interaction control of JSON data.
 * JSON Schema (application/schema+json) has several purposes, one of which is instance validation.
 * The validation process may be interactive or non interactive.
 * For instance, applications may use JSON Schema to build a user interface enabling interactive content generation
 * in addition to user input checking, or validate data retrieved from various sources.
 * This specification describes schema keywords dedicated to validation purposes.
 * Please refer to:
 * http://json-schema.org/latest/json-schema-core.html
 * http://json-schema.org/latest/json-schema-validation.html
 * http://spacetelescope.github.io/understanding-json-schema
 */

#pragma once

#include "json.h"
#include "re.h"
#include <unordered_set>
#include <unordered_map>
#include <ratio>


namespace evm {

using namespace std;

/*
 * JsonSchema validates and stores a given json schema,
 * and then can be used to validate every json data through the json schema.
 */
class JsonSchema {
public:
    JsonSchema(const Json& schema = Json::object()) : schema_(schema) {
        validate_schema(schema_);
    }

    JsonSchema(initializer_list<Json> schema) : JsonSchema(Json(schema)) {}

    explicit JsonSchema(const string& file_name);

    explicit JsonSchema(const char* file_name) : JsonSchema(string(file_name)) {}

    /*JsonSchema(const JsonSchema&) = delete;
    JsonSchema(JsonSchema&&) = delete;*/

    JsonSchema& operator=(const Json& schema) {
        schema_ = schema;
        validate_schema(schema);
        return *this;
    }

    JsonSchema& operator=(initializer_list<Json> schema) {
        *this = Json(schema);
        return *this;
    }

    /*JsonSchema& operator=(const JsonSchema&) = delete;
    JsonSchema& operator=(JsonSchema&&) = delete;*/

    bool validate(const Json& data) const {
        return validate_by_schema(data, schema_);
    }

    string dump() const {
        return schema_.dump(4);
    }

private:
    bool validate_by_schema(const Json &data, const Json &schema) const;

    static bool validate_unique_items(const Json& items);

    void validate_schema(const Json& schema);

    void validate_schema_array(const Json& items);

    static void validate_string_array(const Json& items);

    static void validate_nonnegative_integer(const Json& item);

    void validate_additional_properties(const Json& object);

    Json refer_current_doc(const string& ref_path, const Json& schema);

    Json refer_local_file(const string &ref_file);

    Json schema_;
    unordered_map<string, Json> refer_schemas_;
    unordered_map<string, shared_ptr<re::RegexObject>> res_;
};

}
